package org.apache.dolphinscheduler.api.aspect;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dolphinscheduler.api.configuration.RedisCache;
import org.apache.dolphinscheduler.common.constants.Constants;
import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.dolphinscheduler.dao.vo.PermissionVo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Aspect
@Component
@Slf4j
public class PermissionCheckAspect {

    @Autowired
    private RedisCache redisCache;

    // 切入点表达式决定了用注解方式的方法切还是针对某个路径下的所有类和方法进行切，方法必须是返回void类型
    @Pointcut("@annotation(org.apache.dolphinscheduler.api.aspect.PermissionCheck)")
    private void permissionCheckCut() {
    };

    // 定义了切面的处理逻辑。即方法上加了@PermissionCheck
    @Around("permissionCheckCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
//        log.info("========== 校验权限 ==========");
        // 1.记录日志信息
        Signature signature = pjp.getSignature();
        String className = pjp.getTarget().getClass().getSimpleName();
        String methodName = signature.getName();
//        log.info("className:{},methodName:{}", className, methodName);

        // 2.角色权限校验
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        if (targetMethod.isAnnotationPresent(PermissionCheck.class)) {
            // 获取方法上注解中表明的权限
            PermissionCheck permission = (PermissionCheck) targetMethod.getAnnotation(PermissionCheck.class);
            String permissions = permission.permission();
//            log.info("当前接口需要的用户权限:{}", permissions);
            if (StringUtils.isNotEmpty(permissions)) {
                String[] permissionArray = permissions.split(",");// 接口允许的用户权限
                List<String> list = Arrays.asList(permissionArray);
                HttpServletRequest request =
                        ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                User loginUser = parseLoginInfo(request);
                if (loginUser != null) {
//                    log.info("登录用户：" + loginUser.getUserName());
                    String key = Constants.PERMISSION + ":" + loginUser.getId().toString();
                    List<PermissionVo> permissionList = redisCache.getCacheObject(key);
                    List<String> currentPer = new ArrayList<>();
                    for(PermissionVo permissionVo:permissionList){
                        currentPer.add(permissionVo.getPermission());
                    }
//                    log.info("用户权限：" + JSONObject.toJSONString(permissionList));
                    if (permissionList == null || permissionList.size() == 0) {
                        throw new RuntimeException("无权限");
                    }
                    for (String str : list) {
                        if (currentPer.contains(str)) {
                            return pjp.proceed();
                        }
                    }
                } else {
                    // 如果没有权限,抛出异常,由Spring框架捕获,跳转到错误页面
                    throw new RuntimeException("无权限");
                }
            }
        }
        throw new RuntimeException("无权限");
    }

    private User parseLoginInfo(HttpServletRequest request) {
        User loginUser = (User) (request.getAttribute(Constants.SESSION_USER));
        return loginUser;
    }
}
